/**
 * @file   MATlet.java
 * @author Enrique Madrid <e.madrid@lpcsite.com>
 * @date   Sat Jul 14 08:26:05 2007
 *
 * @brief  Applet.
 *
 */

package com.myt.matp;

import com.myt.matp.utils.*;
import com.myt.matp.core.*;
import com.myt.matp.gui.*;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.Display;


/**
 * Applet.
 *
 * La clase que requiere implementar una solución sobre el framework SIMP debe extender
 * la clase MATlet.
 *
 * Esta clase mantiene una referencia de todos los objetos requeridos para la ejecuación
 * de modulos (Module), la administración de documentos (Document), y la interaccion con
 * el usuario (Gui).
 *
 * Dentro del modelo MVC (Model View Controler), los modelos de datos se implementan al
 * extender la clase Document, los controladores al extender la clase Module, y la vista
 * al crear una instancia de la clase Gui, Dialog, u otra derivada.
 *
 * @author Enrique J. Madrid M.
 * @version %I%, %I%
 * @since 1.0
 */
public class MATlet extends MIDlet implements EventListener {

    private static final int ACTIONS_INIT_SIZE = 8;
    
    public static final int MOD_INIT = 0;  // Inicio del MATlet
    public static final int MOD_KILL = 1;  // Salir inesperadamente.
    public static final int MOD_FAIL = 2;  // Salir con mensaje de error.
    public static final int MOD_QUIT = 3;  // Salir del MATlet
    public static final int MOD_PREF = 4;  // Preferencias del MATlet
    public static final int MOD_AUTH = 5;  // Autenticacion
    public static final int MOD_EXIT = 6;  // Abandonar Autenticacion
    public static final int MOD_PASS = 7;  // Solicitar Password
    public static final int MOD_SYUP = 8;  // Actulizar Archivos
    public static final int MOD_SYCO = 9;  // Enviar Operaciones
    public static final int MOD_SYNC = 10; // Sincronizar
    public static final int MOD_MAIL = 11; // Mostrar Mensaje
    public static final int MOD_SUPP = 12; // Mantenimiento
    public static final int MOD_SUQU = 13; // Mantenimiento
    public static final int MOD_ABOT = 14; // Acerca de
    public static final int MOD_SHEL = 15; // Interfaz de comandos
    public static final int MOD_TIME = 16; // Time!
    public static final int MOD_DEMO = 17; // Demo be happy!
    public static final int MOD_USER = 50; // Modulos para el usuario.   

    // Threads
    protected Driver m_Driver = null;
    protected Worker m_Worker = null;
    protected Scheduler m_Scheduler = null;

    // Vectors
    protected Stack m_PendingActions = null;
    protected Queue m_RecycleActions = null;

    protected String m_QuitMessage;
    protected FontSystem m_FontSystem = null;
        
    // Indica que una instancia del applet se ha ejecutado.
    private boolean m_Started = false;
    
    // Timeout de aplicación.
    private int m_AlarmId;
    private int m_TimeOut;
    
    // Contador del tiempo de inactividad
    private int m_TimeCount = 0;

    // Current Module and GUI
    private GUI m_Gui = null;

    public MATlet() {
	m_AlarmId = this.hashCode();
    }


    /**************************************************************
     *
     * FRAMEWORK
     * 
     **************************************************************/

    private Action createAction(Module back, Module next) {
	Action a = (Action)m_RecycleActions.get();
	if (a == null) {
	    a = new Action();
	}
	a.setBack(back);
	a.setNext(next);
	return a;
    }

    private void deleteAction(Action a) {
	m_RecycleActions.put(a);
    }

    public final Module getCurrentModule() {
	return m_Worker.getCurrentModule();
    }

    public final FontSystem getFontSystem() {
        return m_FontSystem;
    }
    
    protected final void startApp() {
        
        //#ifdef DEBUG_MATLET
        //# System.out.println(getClass().getName() + ".startApp() ENTERED");
        //#endif
        
        if(!m_Started ) {
            //#ifdef DEBUG_MATLET
            //# System.out.println("\tIniciando MATlet ...");
            //#endif

            // Iniciar el servidor de fuentes.
            m_FontSystem = new FontSystem(FontSystem.TYPE_JME);

	    // The Graphics User Interfaz
	    setGui(new GUI(this));
            
            // Configuración de la aplicación
            //m_AppCfg = new AppCfg(this);
                        
            // Iniciar el administrador de recursos etiquetas. (Soporte Multi-idiomas)
            //m_ResLabel = new Resource(this);
            //m_ResLabel.create("labels", Resource.TYPE_STRING, 0);
            
            // Iniciar el administrador de recursos gráficos. (Soporte Multi-idiotas)
            //m_ResImage = new Resource(this);
            //m_ResImage.create("icono", Resource.TYPE_IMAGE, 0);
            
            // Iniciar la cola de acciones éxito/fracaso (Don't worry be hacker!!!).
            m_PendingActions = new Stack(ACTIONS_INIT_SIZE);
            m_RecycleActions = new Queue(ACTIONS_INIT_SIZE);
            
            // Iniciar el administrador de documentos (Modelo MVC).
            //m_Documents = new Documents();

            // Iniciar la hebra para despacho de eventos.
	    m_Driver = new Driver(this);
	    m_Driver.start();

            // Iniciar la hebra para trabajo en segundo plano.
            m_Worker = new Worker(this);
            m_Worker.start();

            // Iniciar la hebra para programación de alarmas.
            m_Scheduler = new Scheduler(this);
            m_Scheduler.start();
            
            // Iniciar la hebra para efectos visuales.
            //m_Renderer = new Renderer(this);
            //m_Renderer.start();
            
            // The GNU KISS philosophy
	    execInit();

            m_Started = true;
            
            //#ifdef DEBUG_MATLET
            //# System.out.println("\tMATlet iniciado!");
            //#endif
        }
        
        //#ifdef DEBUG_MATLET
        //# System.out.println(getClass().getName() + ".startApp() EXITING");
        //#endif        
    }
    
    protected final void pauseApp() {
        //#ifdef DEBUG_MATLET
        //# System.out.println(getClass().getName() + ".pauseApp() ENTERED");
        //#endif        
        
        //#ifdef DEBUG_MATLET
        //# System.out.println(getClass().getName() + ".pauseApp() EXITING");
        //#endif        
    }
    
    protected final void destroyApp(boolean unconditional) {
        Thread theCurrentThread;
        
        //#ifdef DEBUG_MATLET
        //# System.out.println(getClass().getName() + ".destroyApp(boolean) ENTERED");
        //#endif
        
        if (m_Started) {
            
            //#ifdef DEBUG_MATLET
            //# System.out.println("\tterminando MATlet ...");
            //#endif
            
	    // Detener la hebra para despacho de eventos
	    m_Driver.postMsgQuit();
            // Detener la hebra para trabajo en segundo plano.
            m_Worker.postMsgQuit();
            // Detener la hebra para programación de alarmas.
            m_Scheduler.postMsgQuit();
            // Detener la hebra para efectos visuales.
            //m_Renderer.stop();
            
            // Recuperando la hebra del rms.
            theCurrentThread = Thread.currentThread();
            try {
                // Esperando las hebras terminen ...
                theCurrentThread.sleep(500);
            } catch(InterruptedException ie) {
                //#ifdef DEBUG_MATLET
                //# System.out.println("\tInterruptedException");
                //#endif
            } finally {
                
                // Liberar recursos del configuración de la aplicación
                //m_AppCfg = null;
                
                // Free Font system resources
                m_FontSystem = null;
                
                // Liberar recursos del administrador de recursos etiquetas. (Soporte Multi-idiomas)
                //m_ResLabel = null;
                
                // Liberar recursos del recursos gráficos. (Soporte Multi-idiotas)
                //m_ResImage = null;
                
                // Liberar recursos de la cola de acciones éxito/fracaso (Don't worry be hacker!!!).
                m_PendingActions = null;
		m_RecycleActions = null;
                
                // Liberar recursos del administrador de documentos (Modelo MVC).
                //m_Documents = null;

                // Liberar recursos de la hebra para despacho de eventos.
		m_Driver = null;

                // Liberar recursos de la hebra para trabajo en segundo plano.
                m_Worker = null;
                
                // Liberar recursos de la la hebra para programación de alarmas.
                m_Scheduler = null;
                
                // Liberar recursos de la hebra para efectos visuales.
                //m_Renderer = null;                

		// The Graphics User Interface
		setGui(null);
            }
            
            // Hasta la vista baby!!!
            m_Started = false;
            
            //#ifdef DEBUG_MATLET
            //# System.out.println("\tMATlet terminado!");
            //#endif
            
        }

        //#ifdef DEBUG_MATLET
        //# System.out.println(getClass().getName() + ".destroyApp(boolean) EXITING");
        //#endif        
    }

    protected void execInit() {
	exec(createModule(MATlet.MOD_INIT));
    }


    public final void kill() {
        destroyApp(true);
        notifyDestroyed();
    }


    /**************************************************************
     *
     * EVENTOS DEL SISTEMA
     * 
     **************************************************************/

    public boolean sendEvent(final Event e) {
	return m_Driver.sendEvent(e);
    }

    public boolean sendEvent(int id, int wParam, long dwParam, Object obj) {
	return m_Driver.sendEvent(id, wParam, dwParam, obj);
    }

    public void postEvent(final Event e) {
	m_Driver.postEvent(e);
    }

    public void postEvent(int id, int wParam, long dwParam, Object obj) {
	m_Driver.postEvent(id, wParam, dwParam, obj);
    }

    public boolean dispatchEvent(final Event e) {
	boolean ret = false;
        switch (e.getId()) {
	case Event.UI_KEY_PRESSED:
	    m_TimeCount = 0;
	    break;
	case Event.SC_ALARM:
	    ret = true;
	    ((EventListener)(e.getObject())).dispatchEvent(e);
	    break;
	}	
	return ret;
    }

    /**************************************************************
     *
     * INTERFAZ GRÁFICA DE USUARIO
     * 
     **************************************************************/
    
    /** 
     * Establece la Interfaz Gráfica de Usuario a utilizar.
     * 
     * @param gui Una referencia aun objeto de la clase GUI.
     */

    public final void setGui(GUI gui) {        
	m_Gui = gui;
	if (m_Gui != null) {
	    Display.getDisplay(this).setCurrent(m_Gui);
	}
    }

    public final GUI getGui() {
	return m_Gui;
    }

    /**************************************************************
     *
     * ALARMAS
     * 
     **************************************************************/

    
    public void initAlarm(int id, long period, EventListener e) {
	m_Scheduler.initAlarm(id, period, e);
    }

    public void killAlarm(int id) {
	m_Scheduler.killAlarm(id);
    }



    /**************************************************************
     *
     * RECURSOS
     * 
     **************************************************************/


    /** 
     * Devuelve una imagen de administrador de Recursos.
     * 
     * @param id Idenfificador del recurso.
     * 
     * @return Si tiene éxito devuelve una referencia a un objeto del tipo Image.
     *         en caso contrario devuelve null.
     */
    /*
    public final Image getImage(int id) {
        return (Image)m_ResImage.getResource(id);
    }
    */

    /** 
     * Devuelve una candena de caracteres del administrador de recursos.
     * 
     * @param id Identificador del recurso.
     * 
     * @return Si tiene éxito devuelve un referencia a un objeto del tipo String.
     *         en caso contrario devuelve null.
     */
    /*
    public final String getLabel( int id ) {
        return (String)m_ResLabel.getResource(id);
    }
    */   

    
    /**************************************************************
     *
     * MODULOS
     * 
     **************************************************************/

    public final void work(Module module, int state) {
	m_Worker.postMsgExec(module);
	m_Worker.postMsgTask(state);
    }

    public Module createModule(int mod_id) {
        Module m = null;

        return m;
    }

    public final void exec(Module module) {
	work(module, Executable.STATE_INIT);
    }

    /** 
     * Notifica al framework que la ejecución del módulo a fracasado.
     *
     * @see MATlet::execQueue()
     */    
    public final void execBack() {
	Action a;
	Module m;
	a = (Action)m_PendingActions.get();
	m = a.getBack();
        work(m, Executable.STATE_BACK);
    }
    
    /** 
     * Notifica al framework que la ejecución del módulo a tenido éxito.
     * 
     * @see MATlet::execQueue()
     */
    public final void execNext() {
	Action a;
	Module m;
	a = (Action)m_PendingActions.get();
	m = a.getNext();
        work(m, Executable.STATE_NEXT);
    }

    /** 
     * Registra los módulos a ejecutar en caso de éxito o fracaso.
     *
     * Este método registra los módulos que serán ejecutados si un módulo 
     * notifica al framework que su ejecución ha tenido éxito o que ha
     * fracasado.
     * 
     * @param back Módulo a ejecutar en caso de fracaso.
     * @param next Módulo a ejecutar en caso de éxito.
     * @see MATlet::createModule()
     * @see MATlet::exec()
     */    
    public final void execQueue(Module back, Module next) {
        m_PendingActions.put(createAction(back, next));
    }
        
    public final void execFail(final String message) {
        //#ifdef DEBUG_MATLET
        //# System.out.println("MATlet::execFail() ENTERED : " + message);
        //#endif
        // Establece una el contenido del mensaje en una variable de MATlet
        m_QuitMessage = message;
        
        // Ejecuta el modulo Fail
        exec(createModule(MOD_FAIL));
        
        //#ifdef DEBUG_MATLET
        //# System.out.println("MATlet::execFail() EXITING");
        //#endif
        
    }
    
    /**
     * Establece el máximo tiempo que la aplicación puede permanecer si utilizar.
     *
     * @param timeout Valor en segundos que la aplicación puede permancer sin ser utilizada.
     *                este valor debe ser mayor que 60 segundos (1 minuto), en consideración
     *                al tiempo que puede demorar la aplicación en proceso de acceso Streams
     *                de datos de disco o la red. Un valor menor que 60 desactiva el control.
     */
    /*
    public final void setTimeOut(int timeout) {
        if (timeout > 60) {
            m_TimeOut = timeout;
            // Notificar al simlet cada 1 segundo
            initAlarm(m_AlarmId, 1000, 0, this);
        }
	else {
	    killAlarm(m_AlarmId);
	}
    }
    */
    

    /**************************************************************
     *
     * UTILIDADES
     * 
     **************************************************************/

    /*

    public final int currentTimeSeconds() {
        return ((int)(System.currentTimeMillis() / 1000));
    }    

    public final String decoding(byte[] bytes, int start, int length) {
	return Utils.decoding(bytes, start, length, getAppEncoding());
    }

    public final byte[] encoding(final String string) {
	return Utils.encoding(string, getAppEncoding());
    }
    */

    /**************************************************************
     *
     * CONFIGURACION
     * 
     **************************************************************/
    /*

    public final void appCfgLoadFromFile() {
        m_AppCfg.loadFromFile();
    }

    protected final boolean getAppCfgCommit() {
	return m_AppCfg.m_Commit;
    }

    protected final int getAppCfgSyncPer() {
	return m_AppCfg.m_SyncPer;
    }

    protected final int getAppCfgTimeOut() {
	return m_AppCfg.m_TimeOut;
    }

    protected final boolean getAppCfgBatch() {
	return m_AppCfg.m_Batch;
    }

    public final String getAppEncoding() {
	return m_AppCfg.m_Encoding;
    }
    */

}
